﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LPLib
{
    /// <summary>
    /// States for a method that reads a fastq file.
    /// </summary>
    public enum FastqReaderState
    {
        /// <summary>
        /// The initial state 
        /// </summary>
        Start,
        /// <summary>
        /// The state indicating that the sequence is being read.
        /// </summary>
        ReadingSeq,
        /// <summary>
        /// The state indicating that the quality scores are being read.
        /// </summary>
        ReadingQScore,
        /// <summary>
        /// The state indicating that nothing is happening.
        /// </summary>
        Waiting
    };

    public class Reader<T>
    {
        protected MonomerEncoding<T> encoding;
        private StringBuilder message = new StringBuilder();
        public string Message
        {
            get { return message.ToString(); }
        }

        public Reader(MonomerEncoding<T> encoding)
        {
            this.encoding = encoding;
        }

        public Dictionary<string, Polymer<T>> ReadFASTA(string fastaString)
        {
            Dictionary<string, Polymer<T>> members = new Dictionary<string, Polymer<T>>();
            StringBuilder seq = new StringBuilder();
            string name = string.Empty;
            using (System.IO.StringReader reader = new System.IO.StringReader(fastaString))
            {
                string line = reader.ReadLine();
                while (line != null)
                {
                    if (line == string.Empty)
                    {
                        line = reader.ReadLine();
                        continue; // skip all blank lines
                    }
                    if (line[0] == '>') // this line contains the polymer name
                    {
                        if (seq.Length > 0) // process the preceding polymer sequence
                        {
                            if (!members.ContainsKey(name))
                            {
                                members.Add(name, new Polymer<T>(encoding, name, seq.ToString().ToUpper()));
                            }
                        }
                        seq.Clear();
                        name = line.Substring(1).Trim();
                    }
                    else
                    {
                        foreach (char c in line)
                        {
                            if (!char.IsWhiteSpace(c))
                                seq.Append(c);
                        }
                    }
                    line = reader.ReadLine();
                }
                if (seq.Length > 0)
                {
                    Polymer<T> newPolymer = new Polymer<T>(encoding, name);
                    //Clean(ref newPolynuc.Seq);
                    if (!members.ContainsKey(name))
                    {
                        members.Add(name, new Polymer<T>(encoding, name, seq.ToString().ToUpper()));
                    }
                }
            }
            return members;
        }

        public Dictionary<string, Polymer<T>> ReadFASTQ(string fastqString)
        {
            Dictionary<string, Polymer<T>> members = new Dictionary<string, Polymer<T>>();
            // FASTQ format has the form:
            // 
            // @HWI-EAS209_0006_FC706VJ:5:58:5894:21141#ATCACG/1
            // TTAATTGGTAAATAAATCTCCTAATAGCTTAGATNTTACCTTNNNNNNNNNNTAGTTTCTTGAGATTTGTTGGGGGAGACATTTTTGTGATTGCCTTGAT
            // +HWI-EAS209_0006_FC706VJ:5:58:5894:21141#ATCACG/1
            // efcfffffcfeefffcffffffddf`feed]`]_Ba_^__[YBBBBBBBBBBRTT\]][]dddd`ddd^dddadd^BBBBBBBBBBBBBBBBBBBBBBBB
            //
            // the "@" denotes the beggining of a new entry. Its title fills the rest of that line
            // the next line contains the bases of the sequence
            // the next line begins with "+" and may repeat the title
            // the fourth and final line contains the quality score, given by the byte equivalent of the ascii character
            // with some offset. The offset depends on the sequencing platform used

            int offset = -33;
            FastqReaderState state = FastqReaderState.Start;
            byte[] qual = null;
            StringBuilder seq = new StringBuilder();
            string name = string.Empty;
            uint id = 0;
            using (System.IO.StringReader reader = new System.IO.StringReader(fastqString))
            {
                string line = reader.ReadLine().Trim();
                while (line != null)
                {
                    switch (state)
                    {
                        case FastqReaderState.Start:
                            if (line[0] != '@')
                            {
                                continue;
                            }
                            else
                            {
                                name = line.Substring(1);
                            }
                            state = FastqReaderState.ReadingSeq;
                            break;
                        case FastqReaderState.ReadingSeq:
                            seq.Clear();
                            for (int i = 0; i < line.Length; i++)
                            {
                                seq.Append(line[i]);
                            }
                            state = FastqReaderState.Waiting;
                            break;
                        case FastqReaderState.Waiting:
                            if (line[0] != '+')
                            {
                                throw new Exception("Unexpected input: " + line);
                            }
                            state = FastqReaderState.ReadingQScore;
                            break;
                        case FastqReaderState.ReadingQScore:
                            qual = new byte[line.Length];
                            for (int i = 0; i < line.Length; i++)
                            {
                                qual[i] = (byte)(line[i] + offset);
                            }
                            state = FastqReaderState.Waiting;
                            if (!members.ContainsKey(name))
                            {
                                if (seq.Length == qual.Length)
                                {
                                    members.Add(name, new Polymer<T>(encoding, name, seq.ToString().ToUpper(), qual));
                                }
                                else
                                {
                                    message.AppendLine(name);
                                }
                            }
                            id++;
                            state = FastqReaderState.Start;
                            break;
                    }
                    line = reader.ReadLine();
                }
            }
            
            return members;
        }

        public PolymerCollection<double[]> ReadNPMF(string npmfString)
        {
            throw new NotImplementedException();
            //PolymerCollection<double[]> members = new PolymerCollection<double[]>(new NucleotidePMF());
            
            //return members;
        }
    }

}
